package cn.anecansaitin.jep.parser;

import cn.anecansaitin.jep.Lexer;
import cn.anecansaitin.jep.Token;
import cn.anecansaitin.jep.TokenType;
import cn.anecansaitin.jep.parser.ast.node.NodeCreationResult;
import cn.anecansaitin.jep.parser.ast.node.expression.*;
import cn.anecansaitin.jep.parser.ast.node.expression.literal.*;
import cn.anecansaitin.jep.types.TypeInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class Parser {
    private final Lexer lexer;
    private final Stack<Token> tokens;
    private final List<String[]> errors = new ArrayList<>();
    private Token preToken;
    private Expression expression;
    private boolean hasError;

    public Parser(Lexer lexer) {
        this.lexer = lexer;
        this.tokens = new Stack<>();

        if (lexer.hasError()) {
            hasError = true;
            errors.addAll(lexer.getErrors());
            return;
        }

        List<Token> lexerTokens = lexer.getTokens();

        for (int i = lexerTokens.size() - 1; i >= 0; i--) {
            tokens.push(lexerTokens.get(i));
        }
    }

    public void parse() {
        if (expression != null || hasError || tokens.size() <= 1) {
            return;
        }

        expression = parseExpression(0);
    }

    @Nullable
    private Expression parseExpression(int precedence) {
        Expression left = parseAtomic();

        if (left == null) {
            return null;
        }

        loop:
        while (true) {
            Token middleToken = peek();

            if (middleToken == null) {
                return left;
            }

            TokenType middleType = middleToken.type();

            switch (middleType) {
                case BIT_LEFT, BIT_RIGHT, BIT_RIGHT_UNSIGNED,
                     PLUS, MINUS,
                     MULTIPLY, DIVIDE, MODULO,
                     POWER,
                     OR, AND, EQUAL, NOT_EQUAL,
                     LESS_THAN, GREATER_THAN, LESS_THAN_EQUAL, GREATER_THAN_EQUAL-> {
                    BranchResult result = parseBinary(left, middleToken, precedence);

                    switch (result.type()) {
                        case BREAK -> {
                            break loop;
                        }
                        case LEFT -> left = result.left();
                        case NULL -> {
                            return null;
                        }
                    }
                }
                case QUESTION -> {
                    BranchResult result = parseTernary(left, middleToken, precedence);

                    switch (result.type()) {
                        case BREAK -> {
                            break loop;
                        }
                        case LEFT -> left = result.left();
                        case NULL -> {
                            return null;
                        }
                    }
                }
                case RIGHT_PAREN, COLON -> {
                    break loop;
                }
                case EOF -> {
                    consume();
                    break loop;
                }
                default -> {
                    markError(middleToken, "不是运算符");
                    return null;
                }
            }
        }

        return left;
    }

    @Nullable
    private Expression parseAtomic() {
        Token peek = peek();

        if (peek == null) {
            return null;
        }

        consume();
        TokenType peekType = peek.type();

        return switch (peekType) {
            case UNARY_PLUS, UNARY_MINUS, INC, DEC, NOT, BIT_NOT -> {
                Expression value = parseExpression(peekType.precedence);

                if (value == null) {
                    markError(peek, "非法表达式开始");
                    yield null;
                }

                NodeCreationResult<Unary> result = Unary.create(peek, value, false, peek.line(), peek.column());
                yield result.success() ? result.node() : null;
            }
            case INT_LITERAL -> new IntLiteral(peek);
            case LONG_LITERAL -> new LongLiteral(peek);
            case FLOAT_LITERAL -> new FloatLiteral(peek);
            case DOUBLE_LITERAL -> new DoubleLiteral(peek);
            case CHAR_LITERAL -> new CharLiteral(peek);
            case STRING_LITERAL -> new StringLiteral(peek);
            case TRUE_LITERAL -> new TrueLiteral(peek);
            case FALSE_LITERAL -> new FalseLiteral(peek);
            case LEFT_PAREN -> {
                Expression value = parseExpression(0);

                if (value == null) {
                    markError(peek, "非法的表达式开始");
                    yield null;
                }

                Token rightParen = peek();

                if (rightParen == null) {
                    markError(preToken, "缺少右括号");
                    yield null;
                }

                if (rightParen.type() != TokenType.RIGHT_PAREN) {
                    markError(rightParen, "缺少右括号");
                    yield null;
                }

                consume();
                yield new Grouping(value, peek.line(), peek.column());
            }
            default -> {
                markError(peek, "不是语句");
                yield null;
            }
        };
    }

    @NotNull
    private Parser.BranchResult parseBinary(Expression left, Token middleToken, int precedence) {
        TokenType middleType = middleToken.type();

        if (middleType.precedence < precedence) {
            return BranchResult.BREAK;
        }

        consume();
        Expression right;

        if (left.resultType() == TypeInfo.STRING) {
            // 如果为字符串时，所有加减都先按照字符串处理
            // 思路：字符串只有加号，当为加号优先级以下的运算符时，直接返回左值
            right = parseExpression(12);
        } else {
            right = parseExpression(middleType.precedence);
        }

        if (right == null) {
            markError(middleToken, "非法的表达式开始");
            return BranchResult.NULL;
        }

        NodeCreationResult<Binary> result = Binary.create(left, middleToken, right, middleToken.line(), middleToken.column());

        if (result.success()) {
            return new BranchResult(result.node());
        } else {
            markError(middleToken, result.message());
            return BranchResult.NULL;
        }
    }

    @NotNull
    private Parser.BranchResult parseTernary(Expression left, Token middleToken, int precedence) {
        TokenType middleType = middleToken.type();

        if (middleType.precedence < precedence) {
            return BranchResult.BREAK;
        }

        consume();

        if (left.resultType() != TypeInfo.BOOLEAN) {
            markError(middleToken, "应为布尔值");
            return BranchResult.NULL;
        }

        Expression trueBranch = parseExpression(0);

        if (trueBranch == null) {
            markError(middleToken, "应为表达式");
            return BranchResult.NULL;
        }

        Token colon = peek();

        if (colon == null) {
            markError(preToken, "应为:");
            return BranchResult.NULL;
        }

        consume();
        Expression falseBranch = parseExpression(0);

        if (falseBranch == null) {
            markError(preToken, "应为表达式");
            return BranchResult.NULL;
        }

        NodeCreationResult<Ternary> result = Ternary.create(left, trueBranch, falseBranch, middleToken.line(), middleToken.column());

        if (!result.success()) {
            markError(middleToken, result.message());
            return BranchResult.NULL;
        }

        return new BranchResult(result.node());
    }

    private void markError(Token token, String message) {
        hasError = true;
        String[] error = new String[1];
        error[0] = "[" + token.line() + ":" + token.column() + "] " + (message.isBlank() ? "Parse error" : message);
        errors.add(error);
    }

    @Nullable
    private Token peek() {
        if (tokens.isEmpty()) {
            return null;
        }

        return tokens.peek();
    }

    private void consume() {
        preToken = tokens.pop();
    }

    public boolean hasError() {
        return hasError;
    }

    public Expression getExpression() {
        return expression;
    }

    public List<String[]> getErrors() {
        return errors;
    }

    private record BranchResult(BranchType type, Expression left) {
        public static final BranchResult BREAK = new BranchResult(BranchType.BREAK, null);
        public static final BranchResult NULL = new BranchResult(BranchType.NULL, null);

        public BranchResult(Expression left) {
            this(BranchType.LEFT, left);
        }
    }

    private enum BranchType {
        BREAK,
        LEFT,
        NULL
    }
}
